﻿using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;
using System.Collections.Generic;

namespace Leap.Unity.InputModule {
  public class PhysicsUI : MonoBehaviour {
    [Tooltip("The physically-enabled body of the button")]
    public Transform ButtonFace;
    [Tooltip("OPTIONAL: If you have a dropshadow image that you would like to opacity fade upon compression, add one here")]
    public UnityEngine.UI.Image Shadow;

    private float MaxShadowOpacity;
    private Rigidbody body;
    private SpringJoint SpringJoint;
    private Vector3 InitialLocalPosition;
    private Vector3 PhysicsPosition = Vector3.zero;
    private Vector3 PhysicsVelocity = Vector3.zero;
    private bool physicsOccurred = false;
    private bool isDepressed = false;
    private bool prevDepressed = false;
    private PointerEventData pointerEvent;

    //Reset the Positions of the UI Elements on both Start and Quit
    void Start() {
      if (ButtonFace != null) {
        if (Shadow != null) {
          MaxShadowOpacity = Shadow.color.a;
          Shadow.color = new Color(Shadow.color.r, Shadow.color.g, Shadow.color.b, 0f);
        }

        body = ButtonFace.GetComponent<Rigidbody>();
        SpringJoint = ButtonFace.GetComponent<SpringJoint>();
        InitialLocalPosition = ButtonFace.localPosition;

        pointerEvent = new PointerEventData(EventSystem.current);
        pointerEvent.button = PointerEventData.InputButton.Left;
        RaycastResult result = new RaycastResult();
        result.gameObject = gameObject;
        pointerEvent.pointerCurrentRaycast = result;
        pointerEvent.pointerPress = gameObject;
        pointerEvent.rawPointerPress = gameObject;
        ButtonFace.localPosition = new Vector3(InitialLocalPosition.x, InitialLocalPosition.y, SpringJoint.connectedAnchor.z);
        PhysicsPosition = transform.TransformPoint(new Vector3(InitialLocalPosition.x, InitialLocalPosition.y, SpringJoint.connectedAnchor.z));
        body.position = PhysicsPosition;
      } else {
        Debug.LogWarning("Ensure that you have a UI Element allotted in the Layer Transform!");
      }
    }

    void FixedUpdate() {
      if (!physicsOccurred) {
        physicsOccurred = true;
        body.position = PhysicsPosition;
        body.velocity = PhysicsVelocity;
      }
    }

    void Update() {
      pointerEvent.position = Camera.main.WorldToScreenPoint(ButtonFace.transform.position);
      if (physicsOccurred) {
        physicsOccurred = false;

        Vector3 localPhysicsPosition = transform.InverseTransformPoint(body.position);
        localPhysicsPosition = new Vector3(InitialLocalPosition.x, InitialLocalPosition.y, localPhysicsPosition.z);
        Vector3 newWorldPhysicsPosition = transform.TransformPoint(localPhysicsPosition);
        PhysicsPosition = newWorldPhysicsPosition;

        Vector3 localPhysicsVelocity = transform.InverseTransformDirection(body.velocity);
        localPhysicsVelocity = new Vector3(0f, 0f, localPhysicsVelocity.z);
        Vector3 newWorldPhysicsVelocity = transform.TransformDirection(localPhysicsVelocity);
        PhysicsVelocity = newWorldPhysicsVelocity;

        if (localPhysicsPosition.z > 0) {
          ButtonFace.localPosition = new Vector3(InitialLocalPosition.x, InitialLocalPosition.y, 0f);
          PhysicsVelocity = PhysicsVelocity / 2f;
          isDepressed = true;
        } else if (localPhysicsPosition.z < SpringJoint.connectedAnchor.z * 2f) {
          ButtonFace.localPosition = new Vector3(InitialLocalPosition.x, InitialLocalPosition.y, SpringJoint.connectedAnchor.z * 2f);
          PhysicsPosition = ButtonFace.position;
          isDepressed = false;
        } else {
          ButtonFace.localPosition = localPhysicsPosition;
          isDepressed = false;
        }

        if (SpringJoint && Shadow != null) {
          float LayerHeight = Mathf.Abs(ButtonFace.localPosition.z);
          float RestingHeight = Mathf.Abs(SpringJoint.connectedAnchor.z);
          Shadow.color = new Color(Shadow.color.r, Shadow.color.g, Shadow.color.b, Mathf.Lerp(0f, MaxShadowOpacity, 1 - (Mathf.Abs(LayerHeight - RestingHeight) / RestingHeight)));
        }
      }

      if (isDepressed && !prevDepressed) {
        prevDepressed = true;
        ExecuteEvents.Execute(gameObject, pointerEvent, ExecuteEvents.pointerEnterHandler);
        ExecuteEvents.Execute(gameObject, pointerEvent, ExecuteEvents.pointerDownHandler);
      } else if (!isDepressed && prevDepressed) {
        prevDepressed = false;
        ExecuteEvents.Execute(gameObject, pointerEvent, ExecuteEvents.pointerExitHandler);
        ExecuteEvents.Execute(gameObject, pointerEvent, ExecuteEvents.pointerClickHandler);
        ExecuteEvents.Execute(gameObject, pointerEvent, ExecuteEvents.pointerUpHandler);
      }
    }
  }
}